Store repeating rows in a table field
-
What you'll see
You want a field that holds a table of rows — a repeating list where each row has several columns, such as a list of attributes, contacts, or line items — instead of a single value. You need to know how to set it up in the schema designer, and exactly what the data must look like inside the document when you fill or generate records programmatically.
-
What's actually happening
A table field is a repeating group with two sides that must stay in sync: the schema definition and the record data.
Schema side. In the schema document the field is an entry in
Document.Fields[]withObjectVariant: "Table". Unlike a normal field it has noNameproperty — instead it carries:Array— the name of the property the rows are stored under in each record (e.g."Attributter"). This is how a table field is identified.Columns— an array of{ "Title": "…" }. The column Title becomes the property key on every row. There is no separate ASCII-key / label split like regular fields (Name vs Field); Title does both, so keep it a clean key (Attributt, not a sentence).Rows— a one-row scaffold with one emptyCells[]entry per column. EmptyCells[].Fields[]means a plain text column; typed columns (dropdown, number) are defined by placing a field definition inside a cell.
The designer also generates matching markup in
schema.html: a<table class="table-grid">with a<thead>of column titles and a<tbody data-array="…">whose template cells carrydata-func="input" data-name="<ColumnTitle>".Record side. In each document,
Documentgets a property named exactly the schema’sArrayvalue, holding an array of row objects. Every row has one key per column (the column Title) plus a generatedROWID. A sibling<Array>_typeproperty is present alongside it. -
What to do
1. Add the table field in the schema designer. Add a Table (grid) field, give it an array name, and define its columns. This is a one-time visual step — it writes both the
Fields[]entry and theschema.htmlform for you, kept in sync.2. Name columns as keys, not sentences. The column Title becomes the JSON key on every row, so use
Attributt,Type— not “What the attribute is called”.3. Fill data programmatically as an array of objects. Add a property to
Documentnamed exactly the array name. Each row object holds one key per column Title plus aROWID(uuid-style). Include the sibling<name>_type.// schema Fields[] entry (Table field) { "ObjectVariant": "Table", "Array": "Attributter", "Columns": [ { "Title": "Attributt" }, { "Title": "Type" } ], "Columns_type": "Columns", "Rows": [ { "Cells": [ { "Fields": [], "Fields_type": "Fields" }, { "Fields": [], "Fields_type": "Fields" } ], "Cells_type": "Cells" } ], "Rows_type": "Rows" } // record Document — same key as Array "Attributter": [ { "Attributt": "E-postadresse", "Type": "Tekst", "ROWID": "102f422e-…" }, { "Attributt": "Navn", "Type": "Tekst", "ROWID": "2bff8e62-…" } ], "Attributter_type": "Table"4. Read the rows in a template. In a
.hashdisplay file, loop over the array and use the column titles as keys:#for(let r of Attributter){# <tr><td>#r.Attributt#</td><td>#r.Type#</td></tr> #}#5. The schema definition (
Fields[]) is the source of truth —schema.htmlis a generated artifact. The server rebuildsschema.htmlfromFields[]with no designer round-trip needed. Add a table field toFields[]by hand-editing the schema document with thedoclyCLI, read the document back, and the generated form is already there —<tbody data-array="…">with a template cell per column, and the panel markup for its chapter. So hand-editingFields[]is the whole change: there is no second step to refresh the form, and no window in which the Admin form still shows the old columns. What you must not edit isschema.htmlitself — it is regenerated from the field definitions, so anything written there is discarded.
Last reviewed 10 Aug 2026